package java8;

import java8.pojo.User;

import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IntSummaryStatistics;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.stream.Collectors;

public class CollectorsSample {

    public static void main(String[] args) {
        List<User> users = Arrays.asList(
                new User("Annie", 20),
                new User("Cindy", 18),
                new User("Jason", 19),
                new User("Kelly", 18)
        );
        // ===========================================================
        // 1. 收集元素到容器, 如 Collection(List、Set)、Map、ConcurrentMap
        // ===========================================================
        // 1.1 toList: 将元素收集到 List 列表, 默认是 ArrayList
        // => [20, 18, 19, 18]
        List<Integer> ageList = users.stream().map(User::getAge).collect(Collectors.toList());
        // 1.2 toCollection: 将元素收集到给定类型的容器, 如 LinkedList、TreeSet
        // => [20, 18, 19, 18]
        LinkedList<Integer> ageLinkedList = users.stream()
                .map(User::getAge).collect(Collectors.toCollection(LinkedList::new));
        // 1.3 toSet: 将元素收集到 Set 列表, 默认是 HashSet
        // => [18, 19, 20]
        Set<Integer> ageSet = users.stream().map(User::getAge).collect(Collectors.toSet());
        // 1.4 toMap: 将元素收集到 Map 容器, 第 3、4 个参数为可选参数, 如果 Key 可能重复, 需传入第 3 个参数指定重复时怎么取值
        // 默认是将元素收集到 HashMap 容器中, 如果想用其它实例如 LinkedHashMap, 需传入第 4 个参数
        // => {18=User(name=Cindy, age=18), 19=User(name=Jason, age=19), 20=User(name=Annie, age=20)}
        HashMap<Integer, User> ageMap = users.stream().collect(
                Collectors.toMap(User::getAge, Function.identity(), (u1, u2) -> u1, HashMap::new));
        // 1.5 toConcurrentMap: 与 toMap 类似, toConcurrentMap 是将元素收集到 ConcurrentMap 容器
        // => {18=User(name=Cindy, age=18), 19=User(name=Jason, age=19), 20=User(name=Annie, age=20)}
        ConcurrentMap<Integer, User> ageConcurrentMap = users.stream().collect(
                Collectors.toConcurrentMap(User::getAge, Function.identity(), (u1, u2) -> u1));

        // ===========================================================
        // 2. 计算统计, 如求和、平均数、最大、最小值、元素总个数
        // ===========================================================
        // 2.1 counting: 统计元素总个数
        // => 4
        Long count = users.stream().collect(Collectors.counting());
        // 2.2 summingInt/summingLong/summingDouble: 求和
        // => 75
        Integer ageSum = users.stream().collect(Collectors.summingInt(User::getAge));
        // 2.3 maxBy: 求最大值
        // => Optional[User(name=Annie, age=20)]
        Optional<User> ageMax = users.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge)));
        // 2.4 minBy: 求最小值
        // => Optional[User(name=Cindy, age=18)]
        Optional<User> ageMin = users.stream().collect(Collectors.minBy(Comparator.comparing(User::getAge)));
        // 2.5 averagingInt/averagingLong/averagingDouble: 求平均数
        // => 18.75
        Double ageAverage = users.stream().collect(Collectors.averagingInt(User::getAge));
        // 2.6 summarizingInt/summarizingLong/summarizingDouble: 统计(包括元素总数、元素的值总和、最大、最小值、平均数)
        // => IntSummaryStatistics{count=4, sum=75, min=18, average=18.750000, max=20}
        IntSummaryStatistics ageStatic = users.stream().collect(Collectors.summarizingInt(User::getAge));
        // 2.7 reducing: 元素归集
        // => 76
        Integer ageReduce = users.stream().map(User::getAge).collect(Collectors.reducing(1, (i1, i2) -> i1 + i2));

        // ===========================================================
        // 3. 连接元素
        // ===========================================================
        // 3.1 joining: 将元素连接起来
        // => AnnieCindyJasonKelly
        String nameJoin = users.stream().map(User::getName).collect(Collectors.joining());
        // 3.1 joining: 将元素连接起来, 使用分隔符
        // => Annie,Cindy,Jason,Kelly
        String nameJoinDelimiter = users.stream().map(User::getName).collect(Collectors.joining(","));
        // 3.1 joining: 将元素连接起来, 使用分隔符、前缀、后缀
        // => [Annie,Cindy,Jason,Kelly]
        String nameJoinDelimiterFix = users.stream().map(User::getName).collect(
                Collectors.joining(",", "[", "]"));

        // ===========================================================
        // 4. 分组
        // ===========================================================
        // 4.1 将元素进行分组, 默认是将元素收集到 List 容器中, 并映射到 HashMap
        // => {18=[User(name=Cindy, age=18), User(name=Kelly, age=18)], 19=[User(name=Jason, age=19)], 20=[User(name=Annie, age=20)]}
        Map<Integer, List<User>> ageGroup = users.stream().collect(Collectors.groupingBy(User::getAge));
        // 4.1 将元素进行分组, 如果想将元素收集到其它容器如 Set
        // => {18=[User(name=Cindy, age=18), User(name=Kelly, age=18)], 19=[User(name=Jason, age=19)], 20=[User(name=Annie, age=20)]}
        Map<Integer, Set<User>> ageGroupSet = users.stream().collect(Collectors.groupingBy(User::getAge, Collectors.toSet()));
        // 4.1 将元素进行分组, 如果想将结果映射到其它散列表如 LinkedHashMap
        // => {18=[User(name=Cindy, age=18), User(name=Kelly, age=18)], 19=[User(name=Jason, age=19)], 20=[User(name=Annie, age=20)]}
        LinkedHashMap<Integer, List<User>> ageGroupLinkedMap = users.stream().collect(
                Collectors.groupingBy(User::getAge, LinkedHashMap::new, Collectors.toList()));
        // 4.2 和 groupingBy 类似, groupingBy 将结果映射到 Map, groupingByConcurrent 是将结果映射到 ConcurrentMap
        // => {18=[User(name=Cindy, age=18), User(name=Kelly, age=18)], 19=[User(name=Jason, age=19)], 20=[User(name=Annie, age=20)]}
        ConcurrentMap<String, List<User>> ageConcurrMapGroup = users.stream().collect(Collectors.groupingByConcurrent(User::getName));
        // 4.3 将元素和给定的条件匹配, 将true和false结果进行分组
        // => {false=[User(name=Cindy, age=18), User(name=Kelly, age=18)], true=[User(name=Annie, age=20), User(name=Jason, age=19)]}
        Map<Boolean, List<User>> agePartMap = users.stream().collect(Collectors.partitioningBy(u -> u.getAge() > 18));
        // 4.3 将元素和给定的条件匹配, 默认是将元素收集到 List 容器, 如想用其他容器如 Set
        // => {false=[User(name=Cindy, age=18), User(name=Kelly, age=18)], true=[User(name=Annie, age=20), User(name=Jason, age=19)]}
        Map<Boolean, Set<User>> agePartMapSet = users.stream().collect(Collectors.partitioningBy(u -> u.getAge() > 18, Collectors.toSet()));
    }

}